#include "logger.h"
#include "messagebuilder.h"
#include <stdarg.h>
#include "os/mutexlocker.h"
#include "fwapplication.h"

#define MSG_ITEM_POST_TIMEOUT 100

Logger::Logger()
{
}

Logger::Logger(const std::string &name)
{
    m_name = name;
}

Logger::~Logger()
{
    m_thread.RequestInterruption();
}

Logger &Logger::Instance()
{
    static Logger logger;

    return logger;
}

void Logger::ConfigUart(IF_SOC_Uart *uart, bool enabledDMA)
{
    m_uart = uart;
    m_dmaEnabled = enabledDMA;
}

void Logger::StartAsync()
{
    m_thread.SetThreadName("logger");
    m_thread.Start([this](){WriteStreamTask();});
}

void Logger::StopAsync()
{
    m_thread.Stop();
}

MessageBuilder Logger::Output(LoggerLevel level)
{
    auto mb = MessageBuilder(this);
    mb.m_level = level;

    return mb;
}

MessageBuilder Logger::Output(LoggerLevel level, const std::string &tag)
{
    auto mb = MessageBuilder(this);
    mb.m_level = level;
    mb.m_tag = tag;

    return mb;
}

MessageBuilder Logger::Output(LoggerLevel level, 
                              const std::string &fileName, 
                              int line, 
                              const std::string &funcName, 
                              const std::string &tag)
{
    auto mb = MessageBuilder(this);
    mb.m_level = level;
    mb.m_fileName = fileName;
    mb.m_fileLine = line;
    mb.m_funcName = funcName;
    mb.m_tag = tag;

    return mb;
}

void Logger::BeginWriteStream()
{
    m_mutex.Lock();
}

void Logger::EndWriteStream()
{
    m_mutex.UnLock();
}

void Logger::WriteToStream(const char *buf, size_t len, bool lock)
{
    if(buf == nullptr)
        return;
    if(len == 0)
        return;

    if(lock)
    {
        m_mutex.Lock();
    }

    if(Configuration.WriteMode == LoggerWriteMode::ASync && m_thread.IsRunning())
    {
        PostMsgToQueue(buf, len, MSG_ITEM_POST_TIMEOUT);
    }
    else 
    {
        DoWriteToStreamImpl(buf, len);
    }

    if(lock)
    {
        m_mutex.UnLock();
    }
}

void Logger::DoWriteToStreamImpl(const char *buf, size_t len)
{
    if(m_uart == nullptr)
        return;

    if(m_dmaEnabled)
    {
        m_uart->WriteDMA((uint8_t*)buf, len);
    }    
    else
    {
        m_uart->Write((uint8_t*)buf, len);
    }
}

void Logger::PostMsgToQueue(const char *buf, size_t len, int timeout)
{
    MsgItem *item = new MsgItem();
    item->Buffer = new char[len];
    item->Len = len;

    memcpy((void*)item->Buffer, (void*)buf, len);
    m_queue.Post(item, timeout);
}

#define DeleteMsgItem(item) do{\
    delete item;\
    delete item->Buffer;\
}while(0)

void Logger::WriteStreamTask()
{
    //因为DMA发送，没有关联DMA完成中断，不知道什么时候完成，这里本次释放上次的内存
    MsgItem *lastItem = nullptr;
    while (true)
    {
        MsgItem *item = nullptr;
        
        bool ok = m_queue.Wait(item);
        if(!ok)
            continue;
        
        if(m_dmaEnabled)
        {   
            while (m_uart->IsDMAIdle() != true);
            if(lastItem != nullptr)
            {
                //delete
                DeleteMsgItem(lastItem);
            }
            lastItem = item;
            //如果上次DMA未发送完成，可能阻塞
            DoWriteToStreamImpl(item->Buffer, item->Len);
        }
        else
        {
            DoWriteToStreamImpl(item->Buffer, item->Len);
            //delete
            DeleteMsgItem(item);
        }
    }
}
